package com.heima.article.job;

import com.alibaba.fastjson.JSON;
import com.heima.common.exception.CustException;
import com.heima.feigns.ArticleFeign;
import com.heima.model.article.vos.SearchArticleVO;
import com.heima.model.common.constants.article.BehaviorConstans;
import com.heima.model.common.constants.article.HotArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.model.mess.app.AggBehaviorDtoPlus;
import com.heima.model.mess.app.NewBehaviorDTO;
import com.heima.model.wemedia.pojos.WmNewsStatistics;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import groovyjarjarpicocli.CommandLine;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

@Component
@Slf4j
public class UpdateArticleBehaviorJob {
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    ArticleFeign articleFeign;
    @Autowired
    MongoTemplate mongoTemplate;
    @XxlJob("updateArticleBehaviorJob")
    public ReturnT updateArticleBehaviorHandler(String params) throws ParseException {
        log.info("文章行为信息统计更新 调度任务开始执行....");
        // 1. 获取 最近10s中  redis list队列中 产生的新行为数据集合
        List<NewBehaviorDTO> newBehaviorList = getRedisBehaviorList();
        if (CollectionUtils.isEmpty(newBehaviorList)){
            log.info(" 项目 太冷清了  ，最近10s中，没人访问我们的项目~~~~~~");
            return ReturnT.SUCCESS;
        }
        //// 2. 分组聚合统计行为数据
        List<AggBehaviorDtoPlus> aggBehaviorList = getAggBehaviorList(newBehaviorList);
        //定时更新文章行为统计信息
        Date time = new Date();
        SimpleDateFormat simp= new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat simp2= new SimpleDateFormat("yyyy-MM-dd");
        Date date = simp2.parse(simp.format(time));
        for (AggBehaviorDtoPlus aggBehaviorDtoPlus : aggBehaviorList) {
            String userId = aggBehaviorDtoPlus.getUserId();
            WmNewsStatistics one = mongoTemplate.findOne(Query.query(Criteria.where("userId").is(userId).and("creatTime").is(date)), WmNewsStatistics.class);
            if (one==null){
                WmNewsStatistics wmNewsStatistics = new WmNewsStatistics();
                wmNewsStatistics.setCreatTime(date);
                wmNewsStatistics.setArticle(aggBehaviorDtoPlus.getArticle());
                wmNewsStatistics.setComment(aggBehaviorDtoPlus.getComment());
                wmNewsStatistics.setUnLikes(aggBehaviorDtoPlus.getUnLikes());
                wmNewsStatistics.setUnFollow(aggBehaviorDtoPlus.getUnFollow());
                wmNewsStatistics.setReadCount(aggBehaviorDtoPlus.getReadCount());
                wmNewsStatistics.setLikes(aggBehaviorDtoPlus.getLikes());
                wmNewsStatistics.setFollow(aggBehaviorDtoPlus.getFollow());
                wmNewsStatistics.setCollection(aggBehaviorDtoPlus.getCollection());
                wmNewsStatistics.setUserId(aggBehaviorDtoPlus.getUserId());
                mongoTemplate.save(wmNewsStatistics);
            }else {
                String id = one.getId();
                Update update = new Update();
                update.set("readCount",one.getReadCount()+aggBehaviorDtoPlus.getReadCount());
                update.set("comment",one.getComment()+aggBehaviorDtoPlus.getComment());
                update.set("fansReadCount",one.getFansReadCount()+aggBehaviorDtoPlus.getFansReadCount());
                update.set("follow",one.getFollow()+aggBehaviorDtoPlus.getFollow());
                update.set("collection",one.getCollection()+aggBehaviorDtoPlus.getCollection());
                update.set("likes",one.getLikes()+aggBehaviorDtoPlus.getLikes());
                update.set("unLikes",one.getUnLikes()+aggBehaviorDtoPlus.getUnLikes());
                update.set("unFollow",one.getUnFollow()+aggBehaviorDtoPlus.getUnFollow());
                update.set("article",one.getArticle()+aggBehaviorDtoPlus.getArticle());
                mongoTemplate.updateMulti(Query.query(Criteria.where("_id").is(id)),update,WmNewsStatistics.class);
            }
        }
        log.info("文章行为信息统计更新 调度任务完成....");
        return ReturnT.SUCCESS;
    }

    private List<AggBehaviorDtoPlus> getAggBehaviorList(List<NewBehaviorDTO> newBehaviorList) {
        List<AggBehaviorDtoPlus> aggBehaviorList = new ArrayList<>();

        //根据文章id查询作者
        for (NewBehaviorDTO newBehaviorDTO : newBehaviorList) {
            ResponseResult<SearchArticleVO> result = articleFeign.findArticle(newBehaviorDTO.getArticleId());
            if (!result.checkCode()){
                CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR);
            }
            SearchArticleVO articleVO = result.getData();
            //获得作者id
            Long authorId = articleVO.getAuthorId();
            newBehaviorDTO.setArticleId(authorId);
        }
        //根据作者id分组
        Map<Long, List<NewBehaviorDTO>> behaviorGroup =
                newBehaviorList.stream().collect(Collectors.groupingBy(NewBehaviorDTO::getArticleId));
        //统计每个分组的数据 计算成一个aggBehaviorPlus
        behaviorGroup.forEach((articleId,behaviorList)->{
            //将每个分组的多个行为数据合并成一个聚合数据
            Optional<AggBehaviorDtoPlus> reduce = behaviorList.stream().map(nweBehavior -> {
                AggBehaviorDtoPlus aggBehaviorDtoPlus = new AggBehaviorDtoPlus();
                aggBehaviorDtoPlus.setUserId(String.valueOf(nweBehavior.getArticleId()));
                switch (nweBehavior.getType()) {
                    case LIKES:
                        aggBehaviorDtoPlus.setLikes(nweBehavior.getAdd());
                        break;
                    case VIEWS:
                        aggBehaviorDtoPlus.setReadCount(nweBehavior.getAdd());
                        break;
                    case FOLLOW:
                        aggBehaviorDtoPlus.setFollow(nweBehavior.getAdd());
                        break;
                    case COMMENT:
                        aggBehaviorDtoPlus.setComment(nweBehavior.getAdd());
                        break;
                    case UNLIKES:
                        aggBehaviorDtoPlus.setUnLikes(nweBehavior.getAdd());
                        break;
                    case UNFOLLOW:
                        aggBehaviorDtoPlus.setUnFollow(nweBehavior.getAdd());
                        break;
                    case FANSVIEWS:
                        aggBehaviorDtoPlus.setFansReadCount(nweBehavior.getAdd());
                        break;
                    case COLLECTION:
                        aggBehaviorDtoPlus.setCollection(nweBehavior.getAdd());
                        break;
                    case PUBLISH:
                        aggBehaviorDtoPlus.setArticle(nweBehavior.getAdd());
                        break;
                    default:
                }
                return aggBehaviorDtoPlus;
            }).reduce(new BinaryOperator<AggBehaviorDtoPlus>() {
                @Override
                public AggBehaviorDtoPlus apply(AggBehaviorDtoPlus a1, AggBehaviorDtoPlus a2) {
                    a1.setLikes(a1.getLikes() + a2.getLikes());
                    a1.setReadCount(a1.getReadCount() + a2.getReadCount());
                    a1.setFollow(a1.getFollow() + a2.getFollow());
                    a1.setComment(a1.getComment() + a2.getComment());
                    a1.setUnLikes(a1.getUnLikes() + a2.getUnLikes());
                    a1.setUnFollow(a1.getUnFollow() + a2.getUnFollow());
                    a1.setFansReadCount(a1.getFansReadCount() + a2.getFansReadCount());
                    a1.setCollection(a1.getCollection() + a2.getCollection());
                    a1.setArticle(a1.getArticle()+a2.getArticle());
                    return a1;
                }
            });
            if (reduce.isPresent()){
                aggBehaviorList.add(reduce.get());
            }
        });
        return aggBehaviorList;


    }

    private List<NewBehaviorDTO> getRedisBehaviorList() {
        // 1. 创建 redis脚本对象
        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
        // 2. 设置 脚本对象(返回类型  脚本地址)
        try {
            redisScript.setResultType(List.class);
            redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis.lua")));
            // 3. redisTemplate执行脚本
            List<String> result = redisTemplate.execute(redisScript, Arrays.asList(BehaviorConstans.ARTICLE_BEHAVIOR_LIST));
            // 4. 封装返回结果
            return result.stream()
                    .map(jsonStr -> JSON.parseObject(jsonStr,NewBehaviorDTO.class))
                    .collect(Collectors.toList());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("执行lua脚本出现异常 , 原因: {}",e.getMessage());
            CustException.cust(AppHttpCodeEnum.SERVER_ERROR,"执行lua脚本出现异常");
            return  null;
        }
    }
}
